package com.gitee.wsl.android.ui.activity

import android.animation.Animator
import android.animation.AnimatorSet
import android.animation.ObjectAnimator
import android.app.Activity
import android.app.SharedElementCallback
import android.content.Context
import android.content.Intent
import android.graphics.Matrix
import android.graphics.RectF
import android.os.Bundle
import android.os.Parcel
import android.os.Parcelable
import android.transition.ChangeBounds
import android.transition.ChangeClipBounds
import android.transition.ChangeImageTransform
import android.transition.ChangeTransform
import android.transition.Fade
import android.transition.Transition
import android.transition.TransitionSet
import android.transition.TransitionValues
import android.util.Property
import android.util.TypedValue
import android.view.View
import android.view.ViewGroup
import android.view.Window
import android.widget.TextView
import androidx.core.app.ActivityCompat
import androidx.core.app.ActivityOptionsCompat
import androidx.core.view.ViewCompat
import com.gitee.wsl.android.R


//https://www.jianshu.com/p/f1946f65109f

object ActivityTransition {

    fun startActivity(activity: Activity, intent: Intent){
        val pairs: MutableList<androidx.core.util.Pair<View, String>> = mutableListOf()
        val options = ActivityOptionsCompat.makeSceneTransitionAnimation(activity, *pairs.toTypedArray())
        ActivityCompat.startActivity(activity, intent, options.toBundle())
    }

    //需要 FEATURE_ACTIVITY_TRANSITIONS
    //在 super.oncreate 之前设置, 或在主题设置
    //activity.window.requestFeature(Window.FEATURE_ACTIVITY_TRANSITIONS)
    fun setUpTransition(
        activity: Activity,
        shareElementInfoList: (() -> List<ShareElementInfo<*>>)?,
        transitionFactory: IShareElementTransitionFactory = DefaultShareElementTransitionFactory(),
    ){

        //是否覆盖执行，其实可以理解成是否同时执行还是顺序执行 false顺序执行
        activity.window.allowEnterTransitionOverlap = true
        activity.window.allowReturnTransitionOverlap = true
        //是否在透明层做动画，false 会受到 其他转场动画影响
        activity.window.sharedElementsUseOverlay = true

        val customEnterTransition = transitionFactory.buildEnterTransition()
        val customExitTransition = transitionFactory.buildExitTransition()
        activity.window.enterTransition = customEnterTransition
        activity.window.exitTransition = customExitTransition
        activity.window.reenterTransition = customExitTransition
        activity.window.returnTransition = customEnterTransition
        //防止状态栏闪烁
        val enterTransition = activity.window.enterTransition
        val exitTransition = activity.window.exitTransition
        if (enterTransition != null) {
            enterTransition.excludeTarget(Window.STATUS_BAR_BACKGROUND_TRANSITION_NAME, true)
            enterTransition.excludeTarget(
                Window.NAVIGATION_BAR_BACKGROUND_TRANSITION_NAME,
                true
            )
        }
        if (exitTransition != null) {
            exitTransition.excludeTarget(Window.STATUS_BAR_BACKGROUND_TRANSITION_NAME, true)
            exitTransition.excludeTarget(Window.NAVIGATION_BAR_BACKGROUND_TRANSITION_NAME, true)
        }

        activity.window.sharedElementEnterTransition = transitionFactory.buildShareElementEnterTransition()
        activity.window.sharedElementExitTransition = transitionFactory.buildShareElementExitTransition()

        activity.setEnterSharedElementCallback(object : SharedElementCallback() {
            override fun onMapSharedElements(
                names: MutableList<String>?,
                sharedElements: MutableMap<String, View>?
            ) {
                mapSharedElements(names, sharedElements, shareElementInfoList)
            }

            override fun onCreateSnapshotView(context: Context?, snapshot: Parcelable?): View? {
                var view : View?= null
                if(snapshot is ShareElementInfo.ShareElementInfoData<*>){
                    view = super.onCreateSnapshotView(context, snapshot.snapShot)
                    ShareElementInfo.ShareElementInfoData.saveToView(view, snapshot)
                } else {
                    view = super.onCreateSnapshotView(context, snapshot)
                }
                return view
            }

            override fun onSharedElementStart(
                sharedElementNames: MutableList<String>?,
                sharedElements: MutableList<View>?,
                sharedElementSnapshots: MutableList<View>?
            ) {
                if(sharedElements?.isNotEmpty() == true && sharedElementSnapshots?.isNotEmpty() == true){
                    val length = sharedElementSnapshots.size
                    for (i in 0 until length){
                        val shareElementView = sharedElements[i]
                        val snapshotView = sharedElementSnapshots[i]
                        var info = ShareElementInfo.ShareElementInfoData.getFromView(snapshotView)
                        if(info == null){
                            info =
                                ShareElementInfo.ShareElementInfoData.getFromView(shareElementView)
                        }
                        info?.updateByView(ShareElementInfo.ShareElementInfoData.DATA_STATUS_START, shareElementView)
                        ShareElementInfo.ShareElementInfoData.saveToView(shareElementView, info)
                    }
                }
            }

            override fun onSharedElementEnd(
                sharedElementNames: MutableList<String>?,
                sharedElements: MutableList<View>?,
                sharedElementSnapshots: MutableList<View>?
            ) {
                if(sharedElements?.isNotEmpty() == true && sharedElementSnapshots?.isNotEmpty() == true){
                    val length = sharedElementSnapshots.size
                    for (i in 0 until length){
                        val shareElementView = sharedElements[i]
                        val snapshotView = sharedElementSnapshots[i]
                        var info = ShareElementInfo.ShareElementInfoData.getFromView(snapshotView)
                        if(info == null){
                            info =
                                ShareElementInfo.ShareElementInfoData.getFromView(shareElementView)
                        }
                        info?.updateByView(ShareElementInfo.ShareElementInfoData.DATA_STATUS_END, shareElementView)
                        ShareElementInfo.ShareElementInfoData.saveToView(shareElementView, info)
                    }
                }
            }


        })
        activity.setExitSharedElementCallback(object : SharedElementCallback() {
            override fun onMapSharedElements(
                names: MutableList<String>?,
                sharedElements: MutableMap<String, View>?
            ) {
                mapSharedElements(names, sharedElements, shareElementInfoList)
            }

            override fun onCaptureSharedElementSnapshot(
                sharedElement: View?,
                viewToGlobalMatrix: Matrix?,
                screenBounds: RectF?
            ): Parcelable {
                val snapshot = super.onCaptureSharedElementSnapshot(sharedElement, viewToGlobalMatrix, screenBounds)
                var clz : Class<ShareElementInfoDataUpdate>? = null
                shareElementInfoList?.invoke()?.let { list ->
                    for (info in list) {
                        if (info.getView() == sharedElement){
                            clz = info.clz as Class<ShareElementInfoDataUpdate>?
                            break
                        }
                    }
                }
                val shareElementInfoData = ShareElementInfo.ShareElementInfoData(snapshot, clz)
                shareElementInfoData.updateByView(ShareElementInfo.ShareElementInfoData.DATA_STATUS_CAPTURE_SNAPSHOT, sharedElement)
                return shareElementInfoData
            }
        })



    }

    private fun mapSharedElements(
        names: MutableList<String>?,
        sharedElements: MutableMap<String, View>?,
        shareElementInfoList: (() -> List<ShareElementInfo<*>>)?,
    ) {
        names?.clear()
        sharedElements?.clear()
        shareElementInfoList?.invoke()?.let { list ->
            for (info in list) {
                val view: View = info.getView()
                ViewCompat.getTransitionName(view)?.let {
                    names?.add(it)
                    sharedElements?.put(it, view)
                }
            }
        }
    }
}

abstract class ShareElementInfoDataUpdate : Parcelable {
    var properties: Bundle? = Bundle()
    abstract fun update(view: View?)
}

class ShareElementInfo<T : ShareElementInfoDataUpdate> {

    var mView: View

    var clz : Class<T>? = null

    constructor(transitionName:String, view: View, clz: Class<T>? = null) {
        this.mView = view
        this.clz = clz
        mView.transitionName = transitionName
    }

    fun getView(): View{
        return mView
    }

    class ShareElementInfoData<T : ShareElementInfoDataUpdate>() : Parcelable {
        var status:String? = ""
        var snapShot: Parcelable? = null
        var clz : Class<T>? = null
        private var customTempData: T? = null
        var customFromData: T? = null
        var customToData: T? = null
        var isEnter: Boolean? = null


        fun updateByView(status:String, shareElementView: View?){
            if(clz != null){
                if(status == DATA_STATUS_CAPTURE_SNAPSHOT){
                    customTempData = clz?.newInstance()
                    customTempData?.update(shareElementView)
                } else if(this.status == DATA_STATUS_CAPTURE_SNAPSHOT && status == DATA_STATUS_START){ //进入新界面
                    customFromData = customTempData
                    isEnter = true
                }else if(this.status == DATA_STATUS_START && status == DATA_STATUS_END){
                    customToData = clz?.newInstance()
                    customToData?.update(shareElementView)
                } else if(this.status == DATA_STATUS_CAPTURE_SNAPSHOT && status == DATA_STATUS_END){ //返回 前一个界面
                    customToData = customTempData
                    isEnter = false
                }else if(this.status == DATA_STATUS_END && status == DATA_STATUS_START){
                    customFromData = clz?.newInstance()
                    customFromData?.update(shareElementView)
                }
            }
            this.status = status
        }

        constructor(parcel: Parcel) : this() {
            this.snapShot = parcel.readParcelable(Parcelable::class.java.classLoader)
            this.customFromData = parcel.readParcelable(Parcelable::class.java.classLoader)
            this.customToData = parcel.readParcelable(Parcelable::class.java.classLoader)
        }

        constructor(snapShot: Parcelable, clz : Class<T>?) : this() {
            this.snapShot = snapShot
            this.clz = clz
        }

        override fun writeToParcel(parcel: Parcel, flags: Int) {
            parcel.writeParcelable(snapShot, flags)
            parcel.writeParcelable(customFromData, flags)
            parcel.writeParcelable(customToData, flags)
        }

        override fun describeContents(): Int {
            return 0
        }

        companion object {
            const val DATA_STATUS_CAPTURE_SNAPSHOT = "onCaptureSharedElementSnapshot"
            const val DATA_STATUS_START = "onSharedElementStart"
            const val DATA_STATUS_END = "onSharedElementEnd"

            fun getFromView(view: View?): ShareElementInfoData<*>? {
                if (view == null) {
                    return null
                }
                val tag = view.getTag(R.id.share_element_info_data)
                return if (tag is ShareElementInfoData<*>) tag else null
            }

            fun saveToView(view: View?, info: ShareElementInfoData<*>?) {
                if (view == null) {
                    return
                }
                view.setTag(R.id.share_element_info_data, info)
            }

            @JvmField val CREATOR: Parcelable.Creator<ShareElementInfoData<ShareElementInfoDataUpdate>> = object : Parcelable.Creator<ShareElementInfoData<ShareElementInfoDataUpdate>> {
                override fun createFromParcel(parcel: Parcel): ShareElementInfoData<ShareElementInfoDataUpdate> {
                    return ShareElementInfoData(parcel)
                }

                override fun newArray(size: Int): Array<ShareElementInfoData<ShareElementInfoDataUpdate>> {
                    return newArray(size)
                }
            }
        }
    }

}


interface IShareElementTransitionFactory {
    fun buildShareElementEnterTransition(): Transition
    fun buildShareElementExitTransition(): Transition
    fun buildEnterTransition(): Transition
    fun buildExitTransition(): Transition
}

class DefaultShareElementTransitionFactory : IShareElementTransitionFactory {
    override fun buildShareElementEnterTransition(): Transition {
        return buildShareElementsTransition()
    }

    override fun buildShareElementExitTransition(): Transition {
        return buildShareElementsTransition()
    }

    protected fun buildShareElementsTransition(): TransitionSet {
        val transitionSet = TransitionSet()
        transitionSet.addTransition(ChangeClipBounds())
        transitionSet.addTransition(ChangeTransform())
        transitionSet.addTransition(ChangeBounds())
        transitionSet.addTransition(ChangeImageTransform())

        transitionSet.addTransition(ChangeTextTransition())

        return transitionSet
    }

    override fun buildEnterTransition(): Transition {
        return Fade()
    }

    override fun buildExitTransition(): Transition {
        return Fade()
    }
}

class ChangeTextTransition : Transition() {

    companion object{
        const val PROPNAME_TEXTSIZE = "ChangeTextTransition::textSize"
        const val PROPNAME_TEXTCOLOR = "ChangeTextTransition::textColor"
    }


    init {
        addTarget(TextView::class.java)
    }

    override fun captureStartValues(transitionValues: TransitionValues) {
        val info = ShareElementInfo.ShareElementInfoData.getFromView(transitionValues.view)
        //注意 此时 还不能确定 info?.customFromData  需要等到 onSharedElementEnd 后才知道 进出方向
        val properties = if (info?.isEnter == true) info.customFromData?.properties else info?.customToData?.properties
        if(properties?.containsKey(PROPNAME_TEXTSIZE) == true && properties.containsKey(
                PROPNAME_TEXTCOLOR
            )){
            println("ChangeTextTransition start View ${transitionValues.view.hashCode()} size = ${properties.get(
                PROPNAME_TEXTSIZE
            )} color = ${properties.get(PROPNAME_TEXTCOLOR)}")
        }
    }

    override fun captureEndValues(transitionValues: TransitionValues) {
        val info = ShareElementInfo.ShareElementInfoData.getFromView(transitionValues.view)
        val properties = if (info?.isEnter == true) info.customToData?.properties else info?.customFromData?.properties
        if(properties?.containsKey(PROPNAME_TEXTSIZE) == true && properties.containsKey(
                PROPNAME_TEXTCOLOR
            )){
            println("ChangeTextTransition end View ${transitionValues.view.hashCode()} size = ${properties.get(
                PROPNAME_TEXTSIZE
            )} color = ${properties.get(PROPNAME_TEXTCOLOR)}")
        }
    }

    override fun createAnimator(
        sceneRoot: ViewGroup,
        startValues: TransitionValues,
        endValues: TransitionValues
    ): Animator? {
        val info = ShareElementInfo.ShareElementInfoData.getFromView(endValues.view)
        if(info?.customFromData?.properties?.containsKey(PROPNAME_TEXTSIZE) == true  && info.customToData?.properties?.containsKey(
                PROPNAME_TEXTSIZE
            ) == true
            && info.customFromData?.properties?.containsKey(PROPNAME_TEXTCOLOR) == true  && info.customToData?.properties?.containsKey(
                PROPNAME_TEXTCOLOR
            ) == true
        ){
            val startTextSize:Float? = info.customFromData?.properties?.get(PROPNAME_TEXTSIZE) as Float?
            val endTextSize:Float? = info.customToData?.properties?.get(PROPNAME_TEXTSIZE) as Float?
            val startTextColor:Int? = info.customFromData?.properties?.get(PROPNAME_TEXTCOLOR) as Int?
            val endTextColor:Int? = info.customToData?.properties?.get(PROPNAME_TEXTCOLOR) as Int?
            println("ChangeTextTransition  createAnimator size:${startTextSize} -> ${endTextSize} color:${startTextColor} -> ${endTextColor}")
            if(startTextSize != null && endTextSize != null && startTextColor != null && endTextColor != null){
                val view = endValues.view as TextView
                val textSizeAnimator: ObjectAnimator = ObjectAnimator.ofFloat<TextView>(
                    view,
                    TextSizeProperty(),
                    startTextSize,
                    endTextSize
                )
                val textColorAnimator: ObjectAnimator = ObjectAnimator.ofArgb<TextView>(
                    view,
                    TextColorProperty(),
                    startTextColor,
                    endTextColor
                )
                val animatorSet = AnimatorSet()
                animatorSet.playTogether(textSizeAnimator, textColorAnimator)
                return animatorSet
            }
        }
        return null
    }

    class TextData() : ShareElementInfoDataUpdate() {

        constructor(parcel: Parcel) : this() {
            properties = parcel.readBundle(javaClass.classLoader)
        }

        override fun writeToParcel(parcel: Parcel, flags: Int) {
            parcel.writeBundle(properties)
        }

        override fun update(view: View?) {
            if(view is TextView){
                properties?.putFloat(PROPNAME_TEXTSIZE, view.textSize)
                properties?.putInt(PROPNAME_TEXTCOLOR, view.currentTextColor)
            }
        }

        override fun describeContents(): Int {
            return 0
        }

        companion object CREATOR : Parcelable.Creator<TextData> {
            override fun createFromParcel(parcel: Parcel): TextData {
                return TextData(parcel)
            }

            override fun newArray(size: Int): Array<TextData?> {
                return newArray(size)
            }
        }

    }

    private class TextSizeProperty : Property<TextView, Float>(Float::class.java, "textSize") {
        override fun set(view: TextView, value: Float?) {
            view.setTextSize(TypedValue.COMPLEX_UNIT_PX, value!!)
        }

        override fun get(view: TextView): Float {
            return view.textSize
        }
    }

    private class TextColorProperty : Property<TextView, Int>(Int::class.java, "textColor") {
        override fun set(view: TextView, value: Int?) {
            view.setTextColor(value!!)
        }

        override fun get(view: TextView): Int {
            return view.currentTextColor
        }
    }

}